﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

//using System.Diagnostics;

namespace InstanceController.RolesPerfMonDAL
{
	/// <summary>
	/// Initialize the Azure Tables. This should be called from your WebRole.cs OnStart method
	/// after all other initializations and immediately prior to the base.OnStart() call.
	/// </summary>
	public class AzureTableInitialization
	{
		// **** SEE EXAMPLE USAGE BELOW TO INITIALIZE THE TABLES ***

		// Recommended in the final build that your table name should be set to a constant particularly
		// if the table will exist for a long time like in a SQL database where table names rarely
		// ever are changed.  This should then prevent accidential name changes on production tables
		// while allowing development subsequent to production releases.

		/// <summary>
		/// This is one and only AzureTableDataConnectionString. Do not remove.
		/// The public version is located in CounterSettingsDataServiceContext.
		/// </summary>
		internal static string AzureTableDataConnectionString;
		/// <summary>
		/// This is one and only CounterSettingsTableName. Do not remove.
		/// The public version is located in CounterSettingsDataServiceContext.
		/// </summary>
		internal static string CounterSettingsTableName;

		// In production code UNcomment line and then set the literals to the desired values.
		// Optionally, use the dynamic setting from the WebRole.cs
		//internal const string AzureTableDataConnectionString = "Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString";

		// If you want to hard code the table name, replace the empty string with a valid Azure table
		// name. CAUTION! will override any other setting if it is not empty.
		// See table storage Azure contraints: http://msdn.microsoft.com/en-us/library/dd179338.aspx
		// Table names must conform to these rules:
		//	Table names may contain only alphanumeric characters.
		//	A table name may not begin with a numeric character. 
		//	Table names are case-insensitive.
		//	Table names must be from 3 through 63 characters long.
		internal const string HardCodeTableName = "ConfigurationTable";

		private static bool _isCloudApplication = true;
		/// <summary>
		/// Gets or sets a value indicating whether this instance is cloud a application.
		/// If is is not as when the appliation is a console app then this must be set to
		/// false or it will throw a storage client error on initialize of tables.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is cloud application; otherwise, <c>false</c>.
		/// </value>
		public static bool IsCloudApplication
		{
			get { return _isCloudApplication; }
			set { _isCloudApplication = value; }
		}

		/// <summary>
		/// Gets or sets the Azure storage account name.
		/// </summary>
		/// <value>The name of the account.</value>
		public static string AccountName { get; set; }
		/// <summary>
		/// Gets or sets the Azure storage account key.
		/// </summary>
		/// <value>The account key.</value>
		public static string AccountKey { get; set; }

		/// <summary>
		/// This method is used if you are not initializing tables and you are not even checking for
		/// table existance.  You must set the connection and table name for the data context.
		/// </summary>
		/// <param name="azureDataConnectionString"></param>
		/// <param name="azureTableName"></param>
		public static void SetAzureTableContextDefaults(string azureDataConnectionString, string azureTableName)
		{
			try
			{
				AzureTableDataConnectionString = azureDataConnectionString;
				CounterSettingsTableName = azureTableName;

				//Override if HardCodeTableName is present.
				if (!String.IsNullOrEmpty(HardCodeTableName))
					CounterSettingsTableName = HardCodeTableName;

				if (String.IsNullOrEmpty(AzureTableDataConnectionString) || String.IsNullOrEmpty(CounterSettingsTableName))
				{
					throw new NullReferenceException(
						"The azure DataConnectionString is missing or there is no table name given. Additionally the DataConnectionString must match your service configuration.");
				}
			}
			catch (NullReferenceException)  //(NullReferenceException nullReferenceException)
			{
				//Debug.Assert(false, nullReferenceException.Message);			
			}
		}

		/// <summary>
		/// Initialize the Azure Tables. This should be done only once to enhance performance for long lived
		/// tables.  In many cases tables will survive years.  Examples are clients, transactions, customers.
		/// 
		/// Add initializations for all your Azure tables here.  This will (should) be called from the WebRole.cs
		/// OnStart method.
		/// </summary>
		/// <remarks>This is the recommended production version.  You should make the modificatins shown
		/// above notes about production code.</remarks>
		public static bool InitializeTables()
		{

			try
			{
				if (String.IsNullOrEmpty(AzureTableDataConnectionString) || String.IsNullOrEmpty(CounterSettingsTableName))
				{
					throw new NullReferenceException("The azure DataConnectionString is missing or there is no table name given. Additionally the DataConnectionString must match your service configuration.");
				}
				var account = GetAccount();
				account.CreateCloudTableClient().CreateTableIfNotExist(CounterSettingsTableName);
				return true;
			}
			catch (NullReferenceException) //(NullReferenceException nullReferenceException)
			{
				//Debug.Assert(false, nullReferenceException.Message);			
				return false;
			}
			catch (Exception)
			{
				return false;
			}
		}

		/// <summary>
		/// Initialize the Azure Tables. This should be done only once to enhance performance for long lived
		/// tables.  In many cases tables will survive years.  Examples are clients, transactions, customers.
		/// 
		/// Add initializations for all your Azure tables here.  This will (should) be called from the WebRole.cs
		/// OnStart method.
		/// </summary>
		/// <remarks>Normally this method is called during development and then changed to the production
		/// version above.</remarks>
		public static bool InitializeTables(string azureDataConnectionString, string azureTableName)
		{
			AzureTableDataConnectionString = azureDataConnectionString;
			CounterSettingsTableName = azureTableName;

			//Override if HardCodeTableName is present.
			if (!String.IsNullOrEmpty(HardCodeTableName))
				CounterSettingsTableName = HardCodeTableName;

			try
			{
				if (String.IsNullOrEmpty(AzureTableDataConnectionString) || String.IsNullOrEmpty(CounterSettingsTableName))
				{
					throw new NullReferenceException("The azure DataConnectionString is missing or there is no table name given. Additionally the DataConnectionString must match your service configuration.");
				}
				var account = GetAccount();
				account.CreateCloudTableClient().CreateTableIfNotExist(CounterSettingsTableName);
				return true;
			}
			catch (NullReferenceException)  //(NullReferenceException nullReferenceException)
			{
				//Debug.Assert(false, nullReferenceException.Message);			
				return false;
			}
			catch (Exception)
			{
				return false;
			}
		}

		public static CloudStorageAccount GetAccount()
		{
			if (String.IsNullOrEmpty(AzureTableDataConnectionString)) return null;

			if (_isCloudApplication)
				return CloudStorageAccount.FromConfigurationSetting(AzureTableDataConnectionString);

			// Create the account object using application settings
			var credentials = new StorageCredentialsAccountAndKey(AccountName, AccountKey);
			return new CloudStorageAccount(credentials, true);
		}

		public static bool DeleteAzureTable(string azureTableName)
		{
			var account = GetAccount();
			if (account == null) return false;

			var deleteTableIfExist = account.CreateCloudTableClient().DeleteTableIfExist(azureTableName);
			return deleteTableIfExist;
		}

		public static List<string> GetListOfAzureTables()
		{
			var account = GetAccount();
			return account != null ? account.CreateCloudTableClient().ListTables().ToList() : null;
		}
	}
}

/*	USAGE: From CloudService Project -Global.asax.cs Application_Start event
 *  Add a Global.asax item to your project if it does not exist. Then replace 
 *  the protected void Application_Start(object sender, EventArgs e) method
 *  with ALL of the code below.
 *  Updated for Azure SDK 1.3
 * 
		protected void Application_Start(object sender, EventArgs e)
		{
			#region Setup CloudStorageAccount Configuration Setting Publisher

			// This code sets up a handler to update CloudStorageAccount instances when their corresponding
			// configuration settings change in the service configuration file.
			CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
			{
				// Provide the configSetter with the initial value
				configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
				RoleEnvironment.Changed += (sender1, arg) =>
				{
					if (arg.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
						.Any(change => (change.ConfigurationSettingName == configName)))
					{
						// The corresponding configuration setting has changed, propagate the value
						if (!configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)))
						{
							// In this case, the change to the storage account credentials in the
							// service configuration is significant enough that the role needs to be
							// recycled in order to use the latest settings. (for example, the 
							// endpoint has changed)
							RoleEnvironment.RequestRecycle();
						}
					}
				};

			});
			#endregion

			#region Start  Table Initialization Section
			// For production code see discussion in AzureTableInit.cs
			// If you hard-code the suggested properties then the following code can be safely removed.
			// Optionally you could hard-code the table names and leave DataConnectionString more
			// dynamic in case you change your Azure account settings for any reason. In that case
			// you would leave all the following code, the table name will simply be replaced with the
			// hard-code version.

			//Initialize settings in your Azure Table Storage objects. You should have initialization for your
			//DataConnectionString and for each table name that you have created.
			OLTPLogs.AzureTableInitialization.SetAzureTableContextDefaults(
				Properties.Settings.Default.AzureDataConnectionString, Properties.Settings.Default.OltpLogsTableName);


			//This creates tables if needed.
			if (!OLTPLogs.AzureTableInitialization.InitializeTables(Properties.Settings.Default.AzureDataConnectionString, Properties.Settings.Default.OltpLogsTableName))
				throw new StorageClientException();
			// If you get an error here there is a problem with your DataConnectionString or with your
			// table name. Check that they exist and that there are not invalid characters in the names.
			// More detail can be gotten by setting breakpoints in the called method.

			// Additional table initializations here.
			// We are not using the "using" statements since the AzureTableInitialization class must be fully qualified
			Clients.AzureTableInitialization.SetAzureTableContextDefaults(
				Properties.Settings.Default.AzureDataConnectionString, Properties.Settings.Default.ClientsTableName);
			if (!Clients.AzureTableInitialization.InitializeTables(Properties.Settings.Default.AzureDataConnectionString, Properties.Settings.Default.ClientsTableName))
				throw new StorageClientException();

			Customers.AzureTableInitialization.SetAzureTableContextDefaults(
				Properties.Settings.Default.AzureDataConnectionString, Properties.Settings.Default.CustomersTableName);
			if (!Customers.AzureTableInitialization.InitializeTables(Properties.Settings.Default.AzureDataConnectionString, Properties.Settings.Default.CustomersTableName))
				throw new StorageClientException();

			#endregion End  Table Initialization Section

		}

*/